import { isNaNOrNull } from './number-util'

export enum LengthUnit {
  M = 'M',
  KM = 'KM',
  NM = 'NM',
  FT = 'FT'
}
export const LengthUnitName: Record<LengthUnit, string> = {
  FT: 'ft',
  M: 'm',
  KM: 'km',
  NM: 'nm'
}

export function lengthUnitName(u: LengthUnit): string {
  return LengthUnitName[u]
}

const LengthConst = {
  KM2M: 1000.0,
  NM2M: 1852,
  FT2M: 0.3048
}

export function ft2m(ft: number): number {
  return ft * 0.3048
}

export function m2ft(m: number): number {
  return m / 0.3048
}

export function km2m(km: number): number {
  return km * 1000.0
}

export function m2km(m: number): number {
  return m / 1000.0
}

export function nm2m(nm: number): number {
  return nm * 1852
}

export function m2nm(m: number): number {
  return m / 1852
}

const toMeterFn: Record<LengthUnit, (v: number) => number> = {
  KM: km2m,
  M: (m) => m,
  NM: nm2m,
  FT: ft2m
}

const fromMeterFn: Record<LengthUnit, (v: number) => number> = {
  KM: m2km,
  M: (m) => m,
  NM: m2nm,
  FT: m2ft
}

export function toMeter(v: number, u: LengthUnit): number {
  return toMeterConverter(u)(v)
}

export function fromMeter(m: number, u: LengthUnit): number {
  return fromMeterConverter(u)(m)
}

export function toMeterConverter(u: LengthUnit): (v: number) => number {
  return toMeterFn[u]
}

export function fromMeterConverter(u: LengthUnit): (v: number) => number {
  return fromMeterFn[u]
}

export function meter2strWithUnit(m: number, u: LengthUnit, fixed: number = 3): string {
  return fromMeter(m, u).toFixed(fixed) + LengthUnitName[u]
}

export function meter2MetricStr(l: number): string {
  if (l < 1000) {
    return Math.floor(l * 100.0) / 100 + 'm'
  }
  l = Math.floor(l)
  return `${l / 1000.0}km`
}

export class LengthModel {
  readonly NONE: number
  constructor(invalid: number) {
    this.NONE = invalid
  }
  public invalid(n: number): boolean {
    return isNaN(n) || n <= -this.NONE || n >= this.NONE
  }

  public valid(n: number): boolean {
    return !this.invalid(n)
  }

  public toMeter(v: number, u: LengthUnit): number {
    if (this.invalid(v)) {
      return this.NONE
    }
    return toMeterConverter(u)(v)
  }

  public fromMeter(m: number, u: LengthUnit): number {
    if (this.invalid(m)) {
      return this.NONE
    }
    return fromMeterConverter(u)(m)
  }

  public str2meter(s: string, u: LengthUnit): number {
    if (!s) {
      return this.NONE
    }
    s = s.trim()
    if (s === '') {
      return this.NONE
    }
    const i = parseFloat(s)
    if (isNaN(i)) {
      return NaN
    }
    if (u === LengthUnit.M) {
      return i
    } else if (u === LengthUnit.KM) {
      return i * LengthConst.KM2M
    } else if (u === LengthUnit.NM) {
      return i * LengthConst.NM2M
    } else {
      return i * LengthConst.FT2M
    }
  }

  public meter2strWithUnit(a: number, u: LengthUnit): string {
    let s = this.meter2str(a, u)
    if (s.length === 0) {
      return s
    }
    return s + LengthUnitName[u]
  }

  public meter2str(a: number, u: LengthUnit): string {
    if (isNaNOrNull(a)) {
      return ''
    }
    if (this.invalid(a)) {
      return ''
    }
    if (u === LengthUnit.M) {
      return a.toFixed(0)
    }
    if (u === LengthUnit.KM) {
      return (a / LengthConst.KM2M).toFixed(3)
    }
    if (u === LengthUnit.NM) {
      return (a / LengthConst.NM2M).toFixed(3)
    }
    return (a / LengthConst.FT2M).toFixed(0)
  }
}

export const LENGTH_NONE = 99999999.0
export const LENGTH = new LengthModel(LENGTH_NONE)
export const ALTITUDE_NONE = 99999.0
export const ALTITUDE = new LengthModel(ALTITUDE_NONE)
